using System;
using System.Collections.Generic;
using Pixume.World.Cubes;

using OpenTK;

namespace Pixume.World.Chunks
{
	//enum for different sides of a cube/block
	public enum Faces
	{
		Front,
		Back,
		Left,
		Right,
		Top,
	}

	public class Chunk
	{
       
		public const int X_CHUNKSIZE = 16-1; //length of each side -1 for 0 indexing
		public const int Y_CHUNKSIZE = 16-1;
		public const int Z_CHUNKSIZE = 16-1;
        public const int MAX_RESOURCES = 128;

		//separate faces by their own lists
		private List<Vector3> mFrontFaces;
		private List<Vector3> mBackFaces;
		private List<Vector3> mLeftFaces;
		private List<Vector3> mRightFaces;
		private List<Vector3> mTopFaces;

		//int value holds block type
		private CubePacket[,,] mCubeList;

		#region Constructors
		public Chunk()
		{
			//TODO must initialize all lists and objects
			mCubeList = new CubePacket[X_CHUNKSIZE,Y_CHUNKSIZE,Z_CHUNKSIZE];
			mFrontFaces = new List<Vector3>();
			mBackFaces = new List<Vector3>();
			mLeftFaces = new List<Vector3>();
			mRightFaces = new List<Vector3>();
			mTopFaces = new List<Vector3>();
		}
		#endregion

        //return block information for specific location
        public CubeType GetCubeType(Vector3 pPosition)
        {
            return GetCubeType((int)pPosition.X, (int)pPosition.Y, (int)pPosition.Z);
        }
        public CubeType GetCubeType(int pX,int pY,int pZ)
        {
            return CubeManager.BlockHandle(mCubeList[pX, pY, pZ].CubeType);
        }

        //TODO allow external change of chunk block (will require re-building)
        public void ChangeBlock(string pType, int pX, int pY, int pZ)
        {
            if (CubeManager.Exists(pType))
            {
                mCubeList[pX, pY, pZ].CubeType = CubeManager.GetBlockID(pType);
            }
            else
            {
                throw new KeyNotFoundException("Chunk: Unable to change block! Cube Type ID [" + pType + "] could not be found!");
            }
        }
        public void ChangeBlock(string pType, Vector3 pPosition)
        {
            ChangeBlock(pType, (int)pPosition.X, (int)pPosition.Y, (int)pPosition.Z);
        }
        public void ChangeBlock(int pType, int pX, int pY, int pZ)
        {
            if (CubeManager.Exists(pType))
            {
                mCubeList[pX, pY, pZ].CubeType = pType;
            }
            else
            {
                throw new KeyNotFoundException("Chunk: Unable to change block! Cube Type ID [" + pType + "] could not be found!");
            }
        }
        public void ChangeBlock(int pType, Vector3 pPosition)
        {
            ChangeBlock(pType, (int)pPosition.X, (int)pPosition.Y, (int)pPosition.Z);
        }

		//chunk receives input to be built
		//needs to be aware of chunks beside it
		public void BuildChunk ()
		{
			//TODO will probably have to import seed and use some sort of algorithm for chunk number and seed
			//temp build with all blocks
			for (int x = 0; x < X_CHUNKSIZE; x++) {
				for (int y = 0; y < Y_CHUNKSIZE;y++){
					for (int z = 0; z < Z_CHUNKSIZE;z++){
						if(CubeManager.Exists ("dirt"))
						{
							mCubeList[x,y,z].CubeType = CubeManager.GetBlockID ("dirt"); //sets all blocks in all chunks to dirt
                            mCubeList[x, y, z].Resources = MAX_RESOURCES;
						}
					}
				}
			}
            UpdateChunk();

		}
		public void UpdateChunk ()
		{
			//TODO update chunk will simply rebuild the chunk from scratch
            ClearPolyList();

            for (int x = 0; x < X_CHUNKSIZE; x++)
            {
                for (int y = 0; y < Y_CHUNKSIZE; y++)
                {
                    for (int z = 0; z < Z_CHUNKSIZE; z++)
                    {
                        if (CubeManager.Exists("dirt"))
                        {
                            SetFace(Faces.Back, x, y, z);
                            SetFace(Faces.Front, x, y, z);
                            SetFace(Faces.Left, x, y, z);
                            SetFace(Faces.Right, x, y, z);
                            SetFace(Faces.Top, x, y, z);
                        }
                    }
                }
            }
		}

        public void ClearPolyList()
        {
            mFrontFaces.Clear();
            mBackFaces.Clear();
            mLeftFaces.Clear();
            mRightFaces.Clear();
            mTopFaces.Clear();
        }
		public void UnloadChunk()
		{
			//TODO will be more involved, must unload gl resources as well

            ClearPolyList();
			mCubeList = null;
		}

		//set face does not check the block type, it must be checked before hand
		private void SetFace (Faces pSetFace,Vector3 pLocation)
		{
			//TODO change code here for polygon changes due to resource value

			//faces must be wound counter-clockwise for proper polygon facing
			//opengl uses left-handed coordinates (right,up, and forward for positive values)
			//2 triangles required per cube face (2 vecotrs will be shared)
			//
			//			Vertex Order                         Texture UV Coords
			//      		
            //      3 4 .-----------. 1                 (0,0) .-----------. (1,0)               
			//          | \         |                         |           |
			//          |    \      |                         |           |
			//          |      \    |                         |           |
			//          |         \ |                         |           |
            //        6 .-----------. 2 5               (0,1) .-----------. (1,1)

			//      	
			//        1 .-----------. 3 4
			//          |         / |
			//          |       /   |
			//          |    /      |
			//          | /         |
			//      2 5 .-----------. 6

			//add vertex face to chunk face group
			switch (pSetFace) {
			case Faces.Back:
				mBackFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y    , pLocation.Z + 1));
				mBackFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y - 1, pLocation.Z + 1));
				mBackFaces.Add (new Vector3(pLocation.X    , pLocation.Y    , pLocation.Z + 1));

				mBackFaces.Add (new Vector3(pLocation.X    , pLocation.Y    , pLocation.Z + 1));
				mBackFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y - 1, pLocation.Z + 1));
				mBackFaces.Add (new Vector3(pLocation.X    , pLocation.Y - 1, pLocation.Z + 1));
				break;
			case Faces.Front:
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y    ,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y - 1,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y    ,pLocation.Z    ));

				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y    ,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y - 1,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y - 1,pLocation.Z    ));
				break;
			case Faces.Left:
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y    ,pLocation.Z + 1));
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y - 1,pLocation.Z + 1));
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y    ,pLocation.Z    ));

				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y    ,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y - 1,pLocation.Z + 1));
				mFrontFaces.Add (new Vector3(pLocation.X    , pLocation.Y - 1,pLocation.Z    ));
				break;
			case Faces.Right:
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y    ,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y - 1,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y    ,pLocation.Z + 1));

				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y    ,pLocation.Z + 1));
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y - 1,pLocation.Z    ));
				mFrontFaces.Add (new Vector3(pLocation.X + 1, pLocation.Y - 1,pLocation.Z + 1));
				break;
			case Faces.Top:
				break;
			}
		}
		//allow faces to be set with integers instead of coordinates structure
		private void SetFace (Faces pSetFace,float pX,float pY,float pZ)
		{
			Vector3 pushCoords;
			pushCoords.X = pX;
			pushCoords.Y = pY;
			pushCoords.Z = pZ;
			SetFace (pSetFace,pushCoords);
		}
	}


	public class ChunkManager
	{

	}
}

